package it.canraid.shapefile;

import it.canraid.shapefile.recordshp.MultiPoint;
import it.canraid.shapefile.recordshp.MultiPointZ;
import it.canraid.shapefile.recordshp.Point;
import it.canraid.shapefile.recordshp.PointM;
import it.canraid.shapefile.recordshp.PointZ;
import it.canraid.shapefile.recordshp.PolyLine;
import it.canraid.shapefile.recordshp.PolyLineM;
import it.canraid.shapefile.recordshp.PolyLineZ;
import it.canraid.shapefile.recordshp.Polygon;
import it.canraid.shapefile.recordshp.PolygonM;
import it.canraid.shapefile.recordshp.PolygonZ;
import it.canraid.shapefile.recordshp.ShapeNull;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class CLoad {
	
	public static EShp loadShp(String fileNameSHP){
		InputStream fshp=null;
		EShp Rshp=new EShp();
		try {
			fshp=new FileInputStream(fileNameSHP);
			byte[] header=new byte[100];
			fshp.read(header);
			readHeaderFile(header, Rshp);
			byte[] headerRecord=new byte[8];
			int nextOffsetWord=50; //in WORD
			while(true){
				int len=fshp.read(headerRecord);
				if (len==8){
					int idRecord=bigLittleInt(headerRecord, 0, 'B');
					int contentLengthRecord=bigLittleInt(headerRecord, 4, 'B');
					byte[] bodyRecord=new byte[contentLengthRecord*2];
					len=fshp.read(bodyRecord);
					if (len==bodyRecord.length){
						ShapeNull oshp=createRecord(bodyRecord);
						oshp.idRecord=idRecord;
						oshp.contentLength=contentLengthRecord;
						Rshp.records.put(nextOffsetWord, oshp);
						nextOffsetWord=nextOffsetWord + (len+8)/2;	//si prepara per il prox offset
					}else
						throw new Exception("SHP: ID_Record:"+idRecord+" len_previsto:"+bodyRecord.length+" len_effettivo:"+len);
				}else
					if (len<=0)
						break;
					else
						throw new Exception("SHP: Header nuovo record: len_previsto:"+8+" len_effettivo:"+len);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			if (fshp!=null){
				try {
					fshp.close();
				} catch (IOException e1) {}
			}
		}
		return Rshp;
	}
	
	public static EDbfComuni2011 loadDbf(String fileNameBDF){
		InputStream fDbf=null;
		EDbfComuni2011 rDbf=null;
		try{
			fDbf=new FileInputStream(fileNameBDF);
			com.bbn.openmap.dataAccess.shape.input.DbfInputStream openMapDbf=new com.bbn.openmap.dataAccess.shape.input.DbfInputStream(fDbf);
			rDbf=new EDbfComuni2011(openMapDbf);
		}catch(Exception e){
			e.printStackTrace();
			if (fDbf!=null){
				try {
					fDbf.close();
				} catch (IOException e1) {}
			}
		}
		return rDbf;
	}
	
	public static EShx loadShx(String fileNameSHX){
		InputStream fshx=null;
		EShx Rshx=null;
		try {
			fshx=new FileInputStream(fileNameSHX);
			byte[] header=new byte[100];
			fshx.read(header);
			int len_essential=bigLittleInt(header, EShapeFile.FILE_LENGTH_BS, EShapeFile.FILE_LENGTH_BO) - 50;//anticipato l'accesso al file, necessaria la lunghezza del file (meno l'HEADER)
			Rshx=new EShx(len_essential/4);//in word
			readHeaderFile(header, Rshx);
			byte[] record=new byte[8];
			int index=-1;
			while(true){
				int len=fshx.read(record);
				index++;
				if (len==8){
					Rshx.offset[index]=bigLittleInt(record, 0, 'B');
					Rshx.length[index]=bigLittleInt(record, 4, 'B');
				}else
					if (len<=0)
						break;
					else
						throw new Exception("SHX: nuovo record: len_previsto:"+8+" len_effettivo:"+len);
			}
			
			if (index!=Rshx.offset.length)
				throw new Exception("SHX: lunghezza file errata: numero record stimato:"+Rshx.offset.length+" numero record raggiunto:"+index);
			
		} catch (Exception e) {
			e.printStackTrace();
			if (fshx!=null){
				try {
					fshx.close();
				} catch (IOException e1) {}
			}
		}
		return Rshx;
	}
	
	private static void readHeaderFile(byte[] header, EShapeFile Rshp){
		if (header.length<100)
			return;
		
		Rshp.fileCode=bigLittleInt(header, EShapeFile.FILE_CODE_BS, EShapeFile.FILE_CODE_BO);
		Rshp.fileLenght=bigLittleInt(header, EShapeFile.FILE_LENGTH_BS, EShapeFile.FILE_LENGTH_BO);
		Rshp.version=bigLittleInt(header, EShapeFile.VERSION_BS, EShapeFile.VERSION_BO);
		Rshp.shapeType=bigLittleInt(header, EShapeFile.SHAPE_TYPE_BS, EShapeFile.SHAPE_TYPE_BO);
		Rshp.Xmin=bigLittleDbl(header, EShapeFile.X_MIN_BS, EShapeFile.X_MIN_BO);
		Rshp.Ymin=bigLittleDbl(header, EShapeFile.Y_MIN_BS, EShapeFile.Y_MIN_BO);
		Rshp.Xmax=bigLittleDbl(header, EShapeFile.X_MAX_BS, EShapeFile.X_MAX_BO);
		Rshp.Ymax=bigLittleDbl(header, EShapeFile.Y_MAX_BS, EShapeFile.Y_MAX_BO);
		Rshp.Zmin=bigLittleDbl(header, EShapeFile.Z_MIN_BS, EShapeFile.Z_MIN_BO);
		Rshp.Zmax=bigLittleDbl(header, EShapeFile.Z_MAX_BS, EShapeFile.Z_MAX_BO);
		Rshp.Mmin=bigLittleDbl(header, EShapeFile.M_MIN_BS, EShapeFile.M_MIN_BO);
		Rshp.Mmax=bigLittleDbl(header, EShapeFile.M_MAX_BS, EShapeFile.M_MAX_BO);
	}
	
	private static int bigLittleInt(byte[] data, int rightIndex, char type){
		if (data==null)
			return -1;
		
		if (data.length-rightIndex<4)
			return -1;
		
		byte[] bd=new byte[4];
		for (int r=0; r<4;r++){
			bd[r]=data[rightIndex+r];
		}
		
		if (type=='b' || type=='B'){
			return ByteBuffer.wrap(bd).order(ByteOrder.BIG_ENDIAN ).getInt();
		}else if (type=='l' || type=='L'){
			return ByteBuffer.wrap(bd).order(ByteOrder.LITTLE_ENDIAN ).getInt();
		}else
			return -1;
	}
	
	private static double bigLittleDbl(byte[] data, int rightIndex, char type){
		if (data==null)
			return -1;
		
		if (data.length-rightIndex<8)
			return -1;
		
		byte[] bd=new byte[8];
		for (int r=0; r<8;r++){
			bd[r]=data[rightIndex+r];
		}
		
		if (type=='b' || type=='B'){
			return ByteBuffer.wrap(bd).order(ByteOrder.BIG_ENDIAN ).getDouble();
		}else if (type=='l' || type=='L'){
			return ByteBuffer.wrap(bd).order(ByteOrder.LITTLE_ENDIAN ).getDouble();
		}else
			return -1;
	}
	
	private static ShapeNull createRecord(byte[] data) throws Exception{
		if (data==null)
			return new ShapeNull();
		
		if (data.length<4)
			return new ShapeNull();
		
		int shapetype=bigLittleInt(data, 0, 'L');
		switch(shapetype){
		case 0:
			return new ShapeNull();
			
		case 1: 
			Point p1=new Point();
			p1.x=bigLittleDbl(data, 4, 'L');
			p1.y=bigLittleDbl(data, 12, 'L');
			return p1;
			
		
		case 3:
			int numParts3=bigLittleInt(data, 36, 'L');
			int numPoints3=bigLittleInt(data, 40, 'L');
			PolyLine pl3=new PolyLine(numParts3, numPoints3);
			pl3.box[0]=bigLittleDbl(data, 4, 'L');
			pl3.box[1]=bigLittleDbl(data, 12, 'L');
			pl3.box[2]=bigLittleDbl(data, 20, 'L');
			pl3.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts3; index++)
				pl3.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints3; index++){
				Point p3=new Point();
				p3.x=bigLittleDbl(data, 44 + (numParts3*4)+ (index*16) , 'L');
				p3.y=bigLittleDbl(data, 44 + (numParts3*4)+ 8 + (index*16) , 'L');
				pl3.points[index]=p3;
			}
			
			int len_calc3=44 + (numParts3*4)+(numPoints3*16);
			if (len_calc3 != data.length)
				throw new Exception("ShapeType: "+pl3.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts3+" numPoints:"+numPoints3+" 44+(numParts*4)+(numPoints*16)="+len_calc3);
			
			return pl3;
			
		case 5:
			int numParts5=bigLittleInt(data, 36, 'L');
			int numPoints5=bigLittleInt(data, 40, 'L');
			Polygon pg5=new Polygon(numParts5, numPoints5);
			pg5.box[0]=bigLittleDbl(data, 4, 'L');
			pg5.box[1]=bigLittleDbl(data, 12, 'L');
			pg5.box[2]=bigLittleDbl(data, 20, 'L');
			pg5.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts5; index++)
				pg5.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints5; index++){
				Point p5=new Point();
				p5.x=bigLittleDbl(data, 44 + (numParts5*4)+ (index*16) , 'L');
				p5.y=bigLittleDbl(data, 44 + (numParts5*4)+ 8 + (index*16) , 'L');
				pg5.points[index]=p5;
			}
			
			int len_calc5=44 + (numParts5*4)+(numPoints5*16);
			if (len_calc5 != data.length)
				throw new Exception("ShapeType: "+pg5.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts5+" numPoints:"+numPoints5+" 44+(numParts*4)+(numPoints*16)="+len_calc5);
			
			return pg5;
			
		case 8:
			int numPoints8=bigLittleInt(data, 36, 'L');
			MultiPoint mp8=new MultiPoint(numPoints8);
			mp8.box[0]=bigLittleDbl(data, 4, 'L');
			mp8.box[1]=bigLittleDbl(data, 12, 'L');
			mp8.box[2]=bigLittleDbl(data, 20, 'L');
			mp8.box[3]=bigLittleDbl(data, 28, 'L');
			
			for (int index=0; index<numPoints8; index++){
				Point p8=new Point();
				p8.x=bigLittleDbl(data, 40 + (index*16) , 'L');
				p8.y=bigLittleDbl(data, 40 + 8 + (index*16) , 'L');
				mp8.points[index]=p8;
			}
			
			int len_calc8=40 +(numPoints8*16);
			if (len_calc8 != data.length)
				throw new Exception("ShapeType: "+mp8.shapetype()+" len_previsto:"+data.length+" numPoints:"+numPoints8+" 40+(numPoints*16)="+len_calc8);
			
			return mp8;
			
		case 11: 
			PointZ p11=new PointZ();
			p11.x=bigLittleDbl(data, 4, 'L');
			p11.y=bigLittleDbl(data, 12, 'L');
			p11.z=bigLittleDbl(data, 20, 'L');
			p11.m=bigLittleDbl(data, 28, 'L');
			return p11;
			
		case 13:
			int numParts13=bigLittleInt(data, 36, 'L');
			int numPoints13=bigLittleInt(data, 40, 'L');
			PolyLineZ pl13=new PolyLineZ(numParts13, numPoints13);
			pl13.box[0]=bigLittleDbl(data, 4, 'L');
			pl13.box[1]=bigLittleDbl(data, 12, 'L');
			pl13.box[2]=bigLittleDbl(data, 20, 'L');
			pl13.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts13; index++)
				pl13.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints13; index++){
				Point p13=new Point();
				p13.x=bigLittleDbl(data, 44 + (numParts13*4)+ (index*16) , 'L');
				p13.y=bigLittleDbl(data, 44 + (numParts13*4)+ 8 + (index*16) , 'L');
				pl13.points[index]=p13;
			}
			
			pl13.zArray[0]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16) , 'L');
			pl13.zArray[1]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16)+8 , 'L');
			for (int index=0; index<numPoints13; index++){
				pl13.zArray[index]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16)+16+(index*8) , 'L');
			}
			
			pl13.mArray[0]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16)+16+(numPoints13*8) , 'L');
			pl13.mArray[1]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16)+16+(numPoints13*8)+8 , 'L');
			for (int index=0; index<numPoints13; index++){
				pl13.mArray[index]=bigLittleDbl(data, 44 + (numParts13*4)+(numPoints13*16)+16+(numPoints13*8)+16+(index*8) , 'L');
			}
			
			
			int len_calc13=44 + (numParts13*4)+(numPoints13*16)+16+(numPoints13*8)+16+(numPoints13*8);
			if (len_calc13 != data.length)
				throw new Exception("ShapeType: "+pl13.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts13+" numPoints:"+numPoints13+" 44+(numParts*4)+(numPoints*16)+16+(numPoints*8)+16+(numPoints*8)="+len_calc13);
			
			return pl13;
			
		case 15:
			int numParts15=bigLittleInt(data, 36, 'L');
			int numPoints15=bigLittleInt(data, 40, 'L');
			PolygonZ pg15=new PolygonZ(numParts15, numPoints15);
			pg15.box[0]=bigLittleDbl(data, 4, 'L');
			pg15.box[1]=bigLittleDbl(data, 12, 'L');
			pg15.box[2]=bigLittleDbl(data, 20, 'L');
			pg15.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts15; index++)
				pg15.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints15; index++){
				Point p15=new Point();
				p15.x=bigLittleDbl(data, 44 + (numParts15*4)+ (index*16) , 'L');
				p15.y=bigLittleDbl(data, 44 + (numParts15*4)+ 8 + (index*16) , 'L');
				pg15.points[index]=p15;
			}
			
			pg15.zArray[0]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16) , 'L');
			pg15.zArray[1]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16)+8 , 'L');
			for (int index=0; index<numPoints15; index++){
				pg15.zArray[index]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16)+16+(index*8) , 'L');
			}
			
			pg15.mArray[0]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16)+16+(numPoints15*8) , 'L');
			pg15.mArray[1]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16)+16+(numPoints15*8)+8 , 'L');
			for (int index=0; index<numPoints15; index++){
				pg15.mArray[index]=bigLittleDbl(data, 44 + (numParts15*4)+(numPoints15*16)+16+(numPoints15*8)+16+(index*8) , 'L');
			}
			
			
			int len_calc15=44 + (numParts15*4)+(numPoints15*16)+16+(numPoints15*8)+16+(numPoints15*8);
			if (len_calc15 != data.length)
				throw new Exception("ShapeType: "+pg15.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts15+" numPoints:"+numPoints15+" 44+(numParts*4)+(numPoints*16)+16+(numPoints*8)+16+(numPoints*8)="+len_calc15);
			
			return pg15;
			
		case 18:
			int numParts18=bigLittleInt(data, 36, 'L');
			int numPoints18=bigLittleInt(data, 40, 'L');
			MultiPointZ mp18=new MultiPointZ(numPoints18);
			mp18.box[0]=bigLittleDbl(data, 4, 'L');
			mp18.box[1]=bigLittleDbl(data, 12, 'L');
			mp18.box[2]=bigLittleDbl(data, 20, 'L');
			mp18.box[3]=bigLittleDbl(data, 28, 'L');
			
			for (int index=0; index<numPoints18; index++){
				Point p18=new Point();
				p18.x=bigLittleDbl(data, 44 + (index*16) , 'L');
				p18.y=bigLittleDbl(data, 44 + 8 + (index*16) , 'L');
				mp18.points[index]=p18;
			}
			
			mp18.zArray[0]=bigLittleDbl(data, 44 + (numPoints18*16) , 'L');
			mp18.zArray[1]=bigLittleDbl(data, 44 + (numPoints18*16)+8 , 'L');
			for (int index=0; index<numPoints18; index++){
				mp18.zArray[index]=bigLittleDbl(data, 44 + (numPoints18*16)+16+(index*8) , 'L');
			}
			
			mp18.mArray[0]=bigLittleDbl(data, 44 + (numPoints18*16)+16+(numPoints18*8) , 'L');
			mp18.mArray[1]=bigLittleDbl(data, 44 + (numPoints18*16)+16+(numPoints18*8)+8 , 'L');
			for (int index=0; index<numPoints18; index++){
				mp18.mArray[index]=bigLittleDbl(data, 44 + (numPoints18*16)+16+(numPoints18*8)+16+(index*8) , 'L');
			}
			
			
			int len_calc18=44 + (numPoints18*16)+16+(numPoints18*8)+16+(numPoints18*8);
			if (len_calc18 != data.length)
				throw new Exception("ShapeType: "+mp18.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts18+" numPoints:"+numPoints18+" 44+(numPoints*16)+16+(numPoints*8)+16+(numPoints*8)="+len_calc18);
			
			return mp18;
			
		case 21:
			PointM p21=new PointM();
			p21.x=bigLittleDbl(data, 4, 'L');
			p21.y=bigLittleDbl(data, 12, 'L');
			p21.m=bigLittleDbl(data, 20, 'L');
			return p21;
			
		case 23:
			int numParts23=bigLittleInt(data, 36, 'L');
			int numPoints23=bigLittleInt(data, 40, 'L');
			PolyLineM pl23=new PolyLineM(numParts23, numPoints23);
			pl23.box[0]=bigLittleDbl(data, 4, 'L');
			pl23.box[1]=bigLittleDbl(data, 12, 'L');
			pl23.box[2]=bigLittleDbl(data, 20, 'L');
			pl23.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts23; index++)
				pl23.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints23; index++){
				Point p23=new Point();
				p23.x=bigLittleDbl(data, 44 + (numParts23*4)+ (index*16) , 'L');
				p23.y=bigLittleDbl(data, 44 + (numParts23*4)+ 8 + (index*16) , 'L');
				pl23.points[index]=p23;
			}
			
			pl23.mArray[0]=bigLittleDbl(data, 44 + (numParts23*4)+(numPoints23*16) , 'L');
			pl23.mArray[1]=bigLittleDbl(data, 44 + (numParts23*4)+(numPoints23*16)+8 , 'L');
			for (int index=0; index<numPoints23; index++){
				pl23.mArray[index]=bigLittleDbl(data, 44 + (numParts23*4)+(numPoints23*16)+16+(index*8) , 'L');
			}
			
			
			int len_calc23=44 + (numParts23*4)+(numPoints23*16)+16+(numPoints23*8);
			if (len_calc23 != data.length)
				throw new Exception("ShapeType: "+pl23.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts23+" numPoints:"+numPoints23+" 44+(numParts*4)+(numPoints*16)+16+(numPoints*8)="+len_calc23);
			
			return pl23;
			
		case 25:
			int numParts25=bigLittleInt(data, 36, 'L');
			int numPoints25=bigLittleInt(data, 40, 'L');
			PolygonM pg25=new PolygonM(numParts25, numPoints25);
			pg25.box[0]=bigLittleDbl(data, 4, 'L');
			pg25.box[1]=bigLittleDbl(data, 12, 'L');
			pg25.box[2]=bigLittleDbl(data, 20, 'L');
			pg25.box[3]=bigLittleDbl(data, 28, 'L');
			for (int index=0; index<numParts25; index++)
				pg25.parts[index]=bigLittleInt(data, 44 + (index*4) , 'L');
			
			for (int index=0; index<numPoints25; index++){
				Point p25=new Point();
				p25.x=bigLittleDbl(data, 44 + (numParts25*4)+ (index*16) , 'L');
				p25.y=bigLittleDbl(data, 44 + (numParts25*4)+ 8 + (index*16) , 'L');
				pg25.points[index]=p25;
			}

			pg25.mArray[0]=bigLittleDbl(data, 44 + (numParts25*4)+(numPoints25*16) , 'L');
			pg25.mArray[1]=bigLittleDbl(data, 44 + (numParts25*4)+(numPoints25*16)+8 , 'L');
			for (int index=0; index<numPoints25; index++){
				pg25.mArray[index]=bigLittleDbl(data, 44 + (numParts25*4)+(numPoints25*16)+16+(index*8) , 'L');
			}
			
			
			int len_calc25=44 + (numParts25*4)+(numPoints25*16)+16+(numPoints25*8);
			if (len_calc25 != data.length)
				throw new Exception("ShapeType: "+pg25.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts25+" numPoints:"+numPoints25+" 44+(numParts*4)+(numPoints*16)+16+(numPoints*8)="+len_calc25);
			
			return pg25;
			
		case 28:
			int numParts28=bigLittleInt(data, 36, 'L');
			int numPoints28=bigLittleInt(data, 40, 'L');
			MultiPointZ mp28=new MultiPointZ(numPoints28);
			mp28.box[0]=bigLittleDbl(data, 4, 'L');
			mp28.box[1]=bigLittleDbl(data, 12, 'L');
			mp28.box[2]=bigLittleDbl(data, 20, 'L');
			mp28.box[3]=bigLittleDbl(data, 28, 'L');
			
			for (int index=0; index<numPoints28; index++){
				Point p28=new Point();
				p28.x=bigLittleDbl(data, 44 + (index*16) , 'L');
				p28.y=bigLittleDbl(data, 44 + 8 + (index*16) , 'L');
				mp28.points[index]=p28;
			}
			
			mp28.mArray[0]=bigLittleDbl(data, 44 + (numPoints28*16) , 'L');
			mp28.mArray[1]=bigLittleDbl(data, 44 + (numPoints28*16)+8 , 'L');
			for (int index=0; index<numPoints28; index++){
				mp28.mArray[index]=bigLittleDbl(data, 44 + (numPoints28*16)+16+(index*8) , 'L');
			}
			
			
			int len_calc28=44 + (numPoints28*16)+16+(numPoints28*8);
			if (len_calc28 != data.length)
				throw new Exception("ShapeType: "+mp28.shapetype()+" len_previsto:"+data.length+" numParts:"+numParts28+" numPoints:"+numPoints28+" 44+(numPoints*16)+16+(numPoints*8)="+len_calc28);
			
			return mp28;
			
		case 31:
			throw new Exception("ShapeType: 31 : not implemented");
			
		default:
			throw new Exception("ShapeType: "+shapetype+" : UNKNOWN!");
			
		}
	}
}
